import { isNil, generateRandomCode } from '../index'

export declare interface ImageGridOptions {
  url: string

  size: number
  width: number
  height: number

  rows: number
  columns: number

  gap: number
  rowGap: number
  columnGap: number

  duration: number

  hoverEffect: boolean
}

export default class ImageGrid {
  constructor(el: HTMLElement, options: Partial<ImageGridOptions>)
  constructor(options: Partial<ImageGridOptions> & { el: HTMLElement })
  constructor(el: any, options?: any) {
    this.init(el, options)
  }

  // 九宫格容器元素
  private el: HTMLElement

  // 九宫格配置
  private options: Partial<ImageGridOptions>

  private code = generateRandomCode()

  private readonly defaultOptions = {
    rows: 3,
    columns: 3,
    size: 300,
    gap: 10,
    duration: 150,
    hoverEffect: true
  }

  // 初始化
  private init(el: HTMLElement, options: Partial<ImageGridOptions>): void
  private init(options: Partial<ImageGridOptions> & { el: HTMLElement }): void
  private init(el: any, options?: any) {
    if (!el) throw new Error(`[Error] arguments[0] is required an HTMLElement or Object with 'el' attribute that is an HTMLElement but got ${el}`)

    if (el instanceof HTMLElement) {
      this.el = el
    } else {
      if (!el.el) throw new Error(`[Error] arguments[0] is required an HTMLElement or Object with 'el' attribute that is an HTMLElement but got ${el.el}`)

      this.el = el.el

      options = el
    }

    this.options = this.getOptions(options)

    this.insertStyleRule()

    this.createGrid()

    this.toggle(true)
  }

  // 获取九宫格配置项
  private getOptions(options: Partial<ImageGridOptions>): Partial<ImageGridOptions> {
    return {
      url: options.url,
      width: options.width || options.size || this.defaultOptions.size,
      height: options.height || options.size || this.defaultOptions.size,
      rows: (options.rows || this.defaultOptions.rows) | 0,
      columns: (options.columns || this.defaultOptions.columns) | 0,
      rowGap: this.getRowGap(options),
      columnGap: this.getColumnGap(options),
      duration: this.getDuration(options),
      hoverEffect: isNil(options.hoverEffect) ? this.defaultOptions.hoverEffect : options.hoverEffect
    }
  }

  // 获取行间距
  private getRowGap(options: Partial<ImageGridOptions>) {
    if (!isNil(options.rowGap)) return options.rowGap

    if (!isNil(options.gap)) return options.gap

    return this.defaultOptions.gap
  }

  // 获取列间距
  private getColumnGap(options: Partial<ImageGridOptions>) {
    if (!isNil(options.columnGap)) return options.columnGap

    if (!isNil(options.gap)) return options.gap

    return this.defaultOptions.gap
  }

  // 获取过渡持续时间
  private getDuration(options: Partial<ImageGridOptions>) {
    if (!isNil(options.duration)) return options.duration

    return this.defaultOptions.duration
  }

  // 设置九宫格样式
  private insertStyleRule() {
    const style = document.createElement('style')

    style.setAttribute('type', 'text/css')

    document.head.appendChild(style)

    const ruleGrid = `[data-grid--${this.code}] {
      padding: 0;
      width: ${this.options.width}px;
      height: ${this.options.height}px;
      display: grid;
      grid-template-rows: repeat(${this.options.rows}, 1fr);
      grid-template-columns: repeat(${this.options.columns}, 1fr);
    }`

    const ruleGridItem = `[data-grid--${this.code}] [data-grid-item--${this.code}] {
      background: url(${this.options.url}) no-repeat;
      background-size: ${this.options.width}px ${this.options.height}px;
      background-position: var(--x, 0) var(--y, 0);
      transform: translate(0, 0);
      transition: all ${this.options.duration / 1000}s ease-in-out;
    }`

    const ruleDivide = `[data-grid--${this.code}][data-grid--divide] [data-grid-item--${this.code}] {
      transform: translate(var(--disX), var(--disY));
    }`

    style.sheet.insertRule(ruleGrid, 0)
    style.sheet.insertRule(ruleGridItem, 0)
    style.sheet.insertRule(ruleDivide, 0)

    if (this.options.hoverEffect) {
      const ruleHover = `[data-grid--${this.code}][data-grid--divide]:hover [data-grid-item--${this.code}] {
        transform: translate(0, 0);
      }`

      style.sheet.insertRule(ruleHover, 0)
    }
  }

  // 创建九宫格
  private createGrid() {
    this.el.innerHTML = ''

    this.el.setAttribute(`data-grid--${this.code}`, '')

    const total = this.options.rows * this.options.columns

    const pageSize = Math.min(this.options.rows, 100)

    let i = 0

    const callback = () => {
      const fragment = document.createDocumentFragment()

      let j = 0

      while (j < pageSize) {
        const gridItem = this.createGridItem(i * pageSize + j)

        fragment.appendChild(gridItem)

        this.el.appendChild(fragment)

        j++
      }

      if ((i * pageSize + j) < total) {
        i++

        requestAnimationFrame(callback)
      }
    }

    callback()
  }

  // 创建九宫格子元素
  private createGridItem(index: number) {
    const gridItem = document.createElement('div')

    const r = (index / this.options.columns) | 0 // 行序号
    const c = index % this.options.columns // 列序号

    const x = -c * this.options.width / this.options.columns + 'px'
    const y = -r * this.options.height / this.options.rows + 'px'

    const disX = (c - ((this.options.columns - 1) / 2)) * this.options.columnGap + 'px'
    const disY = (r - ((this.options.rows - 1) / 2)) * this.options.rowGap + 'px'

    gridItem.setAttribute(`data-grid-item--${this.code}`, '')

    gridItem.style.setProperty('--x', x)
    gridItem.style.setProperty('--y', y)
    gridItem.style.setProperty('--disX', disX)
    gridItem.style.setProperty('--disY', disY)

    return gridItem
  }

  // 切换图片状态
  public toggle(): void
  public toggle(divide: boolean): void
  public toggle(divide?: boolean) {
    if (isNil(divide)) divide = !this.el.hasAttribute('data-grid--divide')

    divide ? this.el.setAttribute('data-grid--divide', '') : this.el.removeAttribute('data-grid--divide')
  }
}